package com.budwk.app.common.config;

import com.alibaba.fastjson.JSONObject;
import com.budwk.app.annotation.ApiIdempotent;
import com.budwk.app.util.poiUtils.nutzBeanUtils.NutzIocUtils;
import com.budwk.starter.security.utils.SecurityUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.InterceptorChain;
import org.nutz.aop.MethodInterceptor;
import org.nutz.ioc.Ioc;
import org.nutz.ioc.aop.SimpleAopMaker;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 接口请求校验 主要实现分布式锁解决幂等性问题
 */

@Slf4j
public class IdempotencyValidate extends SimpleAopMaker<ApiIdempotent> {
    @Override
    public List<? extends MethodInterceptor> makeIt(ApiIdempotent apiIdempotent, Method method, Ioc ioc) {
        return Arrays.asList(new ApiIdMethodInterceptor());
    }
}

@Slf4j
class ApiIdMethodInterceptor implements MethodInterceptor {

    /**
     * 用户必须登陆 才能提交校验
     * @param chain
     * @throws Throwable
     */
    @SneakyThrows
    public void filter(InterceptorChain chain) {
        String param = JSONObject.toJSONString(chain.getArgs());
        if (StringUtils.isEmpty(param)) {
            chain.doChain();
            return;
        }
        // 方法名 + 请求参数 + 当前用户 标识作为唯一
        String cacheKey = String.format("%s%s%s", chain.getCallingMethod().getName(), param, SecurityUtil.getUserId());
        RedissonClient redissonClient = NutzIocUtils.getBean(RedissonClient.class);
        RLock lock = redissonClient.getLock(cacheKey);
        if (lock.isLocked()) {
            log.info("redis key is lock param: {}", cacheKey);
            throw new AppException("任务正在执行中，请勿重复操作");
        }
        try {
            // 默认超时时间是30秒
            lock.lock(30, TimeUnit.SECONDS);
            chain.doChain();
            log.info("方法执行完毕 ---------------------------------> {}", chain.getCallingMethod());
        } catch (Exception e) {
            log.error("error:", e);
            throw new AppException(e.getMessage());
        } finally {
            lock.unlock();
        }
    }
}
